package com.duolebo.uteped_sdk.ota.jl;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.os.Looper;

import androidx.annotation.NonNull;

import com.jieli.jl_bt_ota.constant.StateCode;
import com.jieli.jl_bt_ota.impl.BluetoothOTAManager;
import com.yc.nadalsdk.ble.open.UteBleClient;
import com.yc.nadalsdk.log.LogUtils;
import com.yc.nadalsdk.utils.open.GBUtils;
import com.yc.nadalsdk.utils.open.SPUtil;

import java.util.concurrent.LinkedBlockingQueue;

import io.reactivex.Observable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;

public class JlOtaManager extends BluetoothOTAManager {
   private boolean isDataSend = false;
    public JlOtaManager(Context context) {
        super(context);
    }

    /**
     * 获取已连接的蓝牙设备
     * <p>
     * 注意：1. 是通讯方式对应的蓝牙设备对象<br/>
     * 2. 实时反映设备的连接状态，设备已连接时有值，设备断开时返回null
     * </p>
     */
    @Override
    public BluetoothDevice getConnectedDevice() {
        BluetoothDevice bluetoothDevice = UteBleClient.getUteBleClient().getBluetoothDevice();
        LogUtils.i("jlOTA OtaManager getConnectedDevice bluetoothDevice = " + bluetoothDevice);
        return bluetoothDevice;
    }


    /**
     * 获取已连接的BluetoothGatt对象
     * <p>
     * 若选择BLE方式OTA，需要实现此方法。反之，SPP方式不需要实现
     * </p>
     */
    @Override
    public BluetoothGatt getConnectedBluetoothGatt() {
        BluetoothGatt bluetoothGatt = UteBleClient.getUteBleClient().getBluetoothGatt();
        LogUtils.i("jlOTA OtaManager getConnectedBluetoothGatt  bluetoothGatt = " + bluetoothGatt);
        return bluetoothGatt;
    }

    /**
     * 连接蓝牙设备
     * <p>
     * 注意:1. 目前的回连方式都是回连BLE设备，只需要实现回连设备的BLE
     * 2. 该方法用于设备回连过程，如果客户是双备份OTA或者自行实现回连流程，不需要实现
     * </p>
     *
     * @param device 通讯方式的蓝牙设备
     */
    @Override
    public void connectBluetoothDevice(BluetoothDevice device) {
        UteBleClient.getUteBleClient().connect(device.getAddress());
        LogUtils.i("jlOTA OtaManager connectBluetoothDevice");
    }

    /**
     * 断开蓝牙设备的连接
     *
     * @param device 通讯方式的蓝牙设备
     */
    @Override
    public void disconnectBluetoothDevice(BluetoothDevice device) {
        UteBleClient.getUteBleClient().disconnect();
        LogUtils.i("jlOTA OtaManager disconnectBluetoothDevice");
    }

    /**
     * 发送数据到蓝牙设备
     * <p>
     * 注意: 1. 需要实现可靠的大数据传输<br/>
     * 1.1 如果是BLE发送数据，需要根据MTU进行分包，然后队列式发数，确保数据发出<br/>
     * 1.2 如果是BLE发送数据 而且 协商MTU大于128， 建议发送MTU = 协商MTU - 6， 进行边缘保护
     * 2. 该方法在发送数据时回调，发送的数据是组装好的RCSP命令。一般长度在[10, 525]
     * </p>
     *
     * @param device 已连接的蓝牙设备
     * @param data   数据包
     * @return 操作结果
     */
    @Override
    public boolean sendDataToDevice(BluetoothDevice device, byte[] data) {
        LogUtils.i("jlOTA OtaManager sendDataToDevice data = " + GBUtils.getInstance().bytes2HexString(data));
        addCommand(data);
        if (Looper.getMainLooper().getThread() == Thread.currentThread()){
            LogUtils.i("jlOTA 等待中...主线程");
        }

        //也可以阻塞等待结果
        return true;
    }

    @Override
    public void release() {
        super.release();
        LogUtils.i("jlOTA OtaManager release");
    }

    public void myOnBtDeviceConnection(int status) {
        Disposable disposable = Observable.just(true).observeOn(Schedulers.io()).subscribe(s -> {
            int connectStatus = changeConnectStatus(status);
            BluetoothDevice bluetoothDevice = UteBleClient.getUteBleClient().getBluetoothDevice();
            LogUtils.i("jlOTA 调用了onBtDeviceConnection  status = " + status);
            onBtDeviceConnection(bluetoothDevice, connectStatus);
            if (connectStatus == StateCode.CONNECTION_OK) {
                start();
            }
        });
    }


    public void myOnReceiveDeviceData(byte[] data) {
        BluetoothDevice bluetoothDevice = UteBleClient.getUteBleClient().getBluetoothDevice();
        LogUtils.i("jlOTA onReceiveDeviceData  data = " + GBUtils.getInstance().bytes2HexString(data));
        onReceiveDeviceData(bluetoothDevice, data);
    }


    private int changeConnectStatus(int status) {
        int changeStatus = StateCode.CONNECTION_DISCONNECT;
        switch (status) {
            case BluetoothProfile.STATE_DISCONNECTED:
            case BluetoothProfile.STATE_DISCONNECTING:
                changeStatus = StateCode.CONNECTION_DISCONNECT;
                break;
            case BluetoothProfile.STATE_CONNECTED:
                changeStatus = StateCode.CONNECTION_OK;
                break;
            case BluetoothProfile.STATE_CONNECTING:
                changeStatus = StateCode.CONNECTION_CONNECTING;
                break;
            default:
                break;
        }
        return changeStatus;
    }

    private final LinkedBlockingQueue<JLOtaCommandBean> mQueue = new LinkedBlockingQueue<>();
    JLOtaCommandBean mJLOtaCommandBean;
    public final static int SEND_DATA_MAX_TIMEOUT = 8000; //8 s
    private volatile boolean isThreadWaiting = false;

    public boolean addCommand(byte[] data) {
        LogUtils.i("OTA 添加指令 data = "+GBUtils.getInstance().bytes2HexString(data));
        int mtu = SPUtil.getInstance().getPhoneMtu();
        int dataLen = data.length;
        int blockCount = dataLen / mtu;
        boolean ret = false;
        for (int i = 0; i < blockCount; i++) {
            byte[] mBlockData = new byte[mtu];
            System.arraycopy(data, i * mtu, mBlockData, 0, mBlockData.length);
            if (i == blockCount - 1 && 0 == dataLen % mtu) {
                ret = addSendData(mBlockData, true);
            } else {
                ret = addSendData(mBlockData, false);
            }

        }

        if (0 != dataLen % mtu) {
            byte[] noBlockData = new byte[dataLen % mtu];
            System.arraycopy(data, dataLen - dataLen % mtu, noBlockData, 0, noBlockData.length);
            ret = addSendData(noBlockData, true);
        }

        return ret;
    }

    private boolean addSendData(byte[] data, boolean isEnd) {
        boolean ret = false;
        JLOtaCommandBean bean = new JLOtaCommandBean();
        bean.setData(data);
        bean.setEnd(isEnd);

        try {

            mQueue.put(bean);
            ret = true;
        } catch (InterruptedException e) {
            e.printStackTrace();
            LogUtils.i("添加到队列异常 InterruptedException = " + e);
        }
        if (ret && isThreadWaiting) {
            isThreadWaiting = false;
            synchronized (mQueue) {
                mQueue.notify();
            }
        }
        return ret;
    }


    private void start() {
        LogUtils.i("开始启动指令线程 前 isDataSend = "+isDataSend);
        if (isDataSend){
            return;
        }
        LogUtils.i("开始启动指令线程");
        isDataSend = true;
        synchronized (mQueue) {
            while (isDataSend) {
                isThreadWaiting = false;
                if (mQueue.isEmpty()) {
                    isThreadWaiting = true;
                    LogUtils.i("queue is empty, so waiting for data");
                    try {
                        mQueue.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    mJLOtaCommandBean = mQueue.peek();
                    if (mJLOtaCommandBean != null) {
                        UteBleClient.getUteBleClient().getUteBleConnection().sendDataToJlDevice(mJLOtaCommandBean.getData());

                        try {
                            mQueue.wait(SEND_DATA_MAX_TIMEOUT);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                        if (mJLOtaCommandBean.getEnd()) {
//                            AppDownLatchUtil.getInstance().appLatchNotify(deviceBt3StateLatchList);
                        }
                    }
                    if (!mQueue.isEmpty()) {
                        mQueue.poll();
                    }
                }
            }
        }
        isThreadWaiting = false;
        mQueue.clear();
        LogUtils.i("清除队列");

    }

    public void wakeupSendThread() {
        synchronized (mQueue) {
            mQueue.notify();
        }
    }
}
